Console = {
   preferencesService: Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("xmlapp.helper."),
   _initialized: false,
   _debug: false,
   service: Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService),
   log: function(message) {
      this.service.logStringMessage(message);
   },
   debug: function(message) {
      if (!this._initialized) {
         if (Console.preferencesService) {
            try {
               Console._debug = Console.preferencesService.getBoolPref("debug");
            } catch (ex) {
               // no preference
            }
         }
         this._initialized = true;
      }
      if (this._debug) {
         this.service.logStringMessage(message);
      }
   }
}

function XMLAppHelper() {
   this.initialized = false;
}

XMLAppHelper.prototype.init = function(window) {
   Console.debug("Attaching application for "+window.location.href);
   this.window = window;
   this.service = Components.classes["@milowski.com/xmlapp;1"].getService(Components.interfaces.nsISupports).wrappedJSObject;
   this.info = this.service.pop(window.location.href);
   if (!this.info) {
      Console.log("No document information for "+window.location.href)
   } else {
      //this.document = this.service.xmlIdentity.transformToDocument(this.info.document);
      this.document = this.info.document;
      if (!this.info.app) {
         Console.log("No application found for "+this.info.type);
      } else {
         Console.debug("Loading application for "+this.info.type);
      }
   }

   var current = this;
   this.window.document.addEventListener('unload',function() {
      current.close();
   },false);
   this.initialized = true;
}

XMLAppHelper.prototype.applyTransform = function(name,node) {
   if (!this.info) {
      return null;
   }
   var xform = null;
   for (var i=0; !xform && i<this.info.app.transforms.length; i++) {
      if (this.info.app.transforms[i].role==name) {
         xform = this.info.app.transforms[i];
      }
   }
   if (!xform && this.info.app.transforms.length>0 && name=="start") {
      for (var i=0; !xform && i<this.info.app.transforms.length; i++) {
         if (!this.info.app.transforms[i].role) {
            xform = this.info.app.transforms[i];
         }
      }
   }

   if (xform) {
      return xform.xform.transformToFragment(node ? node : this.info.document,this.window.document);
   } else {
      return null;
   }


}
XMLAppHelper.prototype.firstChild = function(node) {
   return node.firstChild;
}

XMLAppHelper.prototype.importNode = function(owner,node) {
   if (owner.ownerDocument) {
      owner = owner.ownerDocument;
   }
   if (owner.contentType=="text/html") {
      return this.service.htmlIdentity.transformToFragment(node,owner);
   } else {
      return this.service.xmlIdentity.transformToFragment(node,owner);
   }
}

XMLAppHelper.prototype.writeContent = function() {
   if (!this.info.app) {
      // there is no app
      return;
   }
   this.jsContext = {};
   this.jsContext._helper = this;
   this.jsContext.window = this.window;
   this.jsContext.document = this.window.document;
   this.jsContext.Console = Console;
   var jsLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader);
   jsLoader.loadSubScript("chrome://xmlapp/content/unwrap.js",this.jsContext);
   var head = this.window.document.documentElement.firstChild;
   while (head && head.localName!="head") {
      head = head.nextSibling;
   }
   for (var i=0; i<this.info.app.links.length; i++) {
      var link = this.window.document.createElement("link");
      link.setAttribute("rel",this.info.app.links[i].rel);
      link.setAttribute("type",this.info.app.links[i].type);
      link.setAttribute("href",this.info.app.links[i].href);
      head.appendChild(link);
   }
   for (var i=0; i<this.info.app.scripts.length; i++) {
      var spec = this.info.app.scripts[i];
      if (spec.privileged) {
         jsLoader.loadSubScript(spec.src,this.jsContext);
      } else {
         var script = this.window.document.createElement("script");
         script.setAttribute("type",spec.type);
         script.setAttribute("src",spec.src);
         head.appendChild(script);
      }
   }
}

XMLAppHelper.prototype.close = function() {
}

XMLAppHelper.prototype.QueryInterface = function(iid) {
   if (iid.equals(Components.interfaces.nsISupports) ||
       iid.equals(Components.interfaces.nsIXMLAppHelper) ||
       iid.equals(Components.interfaces.nsIClassInfo)) {
      return this;
   }
   throw Components.results.NS_ERROR_NO_INTERFACE;
};

XMLAppHelper.prototype.classDescription = "An XML Application helper class";
XMLAppHelper.prototype.classID = CLASSID;
XMLAppHelper.prototype.contractID = CONTRACTID;
XMLAppHelper.prototype.implementationLanguage = Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT;
XMLAppHelper.prototype.flags = Components.interfaces.nsIClassInfo.DOM_OBJECT;
XMLAppHelper.prototype.getHelperForLanguage = function() { return null; }
XMLAppHelper.prototype.getInterfaces = function(countRef) {
   var interfaces = [Components.interfaces.nsIXMLAppHelper, Components.interfaces.nsIClassInfo, Components.interfaces.nsISupports];
   countRef.value = interfaces.length;
   return interfaces;
}




const CLASSID = Components.ID("{2e93d715-8f49-4118-922a-10fa2d487cd9}");
const CLASSNAME = "XML Application Helper";
const CONTRACTID = "@milowski.com/xmlapp-helper;1";

function GenericComponentFactory(ctor, params) {
  this._ctor = ctor;
  this._params = params;
}
GenericComponentFactory.prototype = {
  _ctor: null,
  _params: null,

  createInstance: function GCF_createInstance(outer, iid) {
    if (outer != null)
      throw Cr.NS_ERROR_NO_AGGREGATION;
    return (new this._ctor(this._params)).QueryInterface(iid);
  },

  QueryInterface: function GCF_QueryInterface(iid) {
    if (iid.equals(Components.interfaces.nsIFactory) ||
        iid.equals(Components.interfaces.nsISupports))
      return this;
    throw Cr.NS_ERROR_NO_INTERFACE;
  }
};


var Module = {
  QueryInterface: function M_QueryInterface(iid) {
    if (iid.equals(Components.interfaces.nsIModule) ||
        iid.equals(Components.interfaces.nsISupports))
      return this;
    throw Components.results.NS_ERROR_NO_INTERFACE;
  },

  getClassObject: function M_getClassObject(cm, cid, iid) {
    if (!iid.equals(Components.interfaces.nsIFactory))
      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;

    return new GenericComponentFactory(XMLAppHelper);
  },

  registerSelf: function M_registerSelf(cm, file, location, type) {
    var cr = cm.QueryInterface(Components.interfaces.nsIComponentRegistrar);

    cr.registerFactoryLocation(CLASSID, CLASSNAME, CONTRACTID,
                               file, location, type);

    var catManager = Components.classes["@mozilla.org/categorymanager;1"].getService(Components.interfaces.nsICategoryManager);
    catManager.addCategoryEntry("JavaScript global constructor","XMLAppHelper",CONTRACTID,true,true);
  },

  unregisterSelf: function M_unregisterSelf(cm, location, type) {
     var catManager = Components.classes["@mozilla.org/categorymanager;1"].getService(Components.interfaces.nsICategoryManager);
     catManager.deleteCategoryEntry("JavaScript global constructor","XMLAppHelper",true);
     var cr = cm.QueryInterface(Components.interfaces.nsIComponentRegistrar);

     cr.registerFactoryLocation(CLASSID, location);
  },

  canUnload: function M_canUnload(cm) {
    return true;
  }
};

function NSGetModule(cm, file) {
  return Module;
}

